package com.gitee.kenewstar.migration.function;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.gitee.kenewstar.migration.constants.Const;
import com.gitee.kenewstar.migration.constants.FunctionCode;
import com.gitee.kenewstar.migration.core.AbstractSyncFunction;
import com.gitee.kenewstar.migration.core.ContextParam;
import com.gitee.kenewstar.migration.entity.CodeRule;
import com.gitee.kenewstar.migration.entity.CodeRuleDetail;
import com.gitee.kenewstar.migration.entity.CodeRuleDist;
import com.gitee.kenewstar.migration.entity.CodeRuleTl;
import com.gitee.kenewstar.migration.entity.CommonResult;
import com.gitee.kenewstar.migration.service.CodeRuleService;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 编码规则function
 *
 * @author kenewstar
 */
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CodeRuleFunction extends AbstractSyncFunction<CodeRule> {

    private final CodeRuleService codeRuleService;

    @Override
    protected Class<CodeRule> parameterType() {
        return CodeRule.class;
    }

    @Override
    public List<CommonResult> execute(ContextParam contextParam, List<CodeRule> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        final List<CodeRule> ruleList = this.codeRuleService.devSelectCodeRuleList(list);
        if (CollUtil.isEmpty(ruleList)) {
            return Collections.emptyList();
        }

        final List<Long> ruleIds = ruleList.stream().map(CodeRule::getRuleId).collect(Collectors.toList());
        final List<CodeRuleTl> codeRuleTls = this.codeRuleService.devSelectCodeRuleTl(ruleIds);
        final List<CodeRuleDist> codeRuleDistList = this.codeRuleService.devSelectCodeRuleDist(ruleIds);
        final List<Long> distIds = codeRuleDistList.stream().map(CodeRuleDist::getRuleDistId).collect(Collectors.toList());
        final List<CodeRuleDetail> codeRuleDetails = this.codeRuleService.devSelectCodeRuleDetail(distIds);

        final Map<Long, List<CodeRuleTl>> codeRuleTlsMap = codeRuleTls.stream().collect(Collectors.groupingBy(CodeRuleTl::getRuleId));
        final Map<Long, List<CodeRuleDist>> codeRuleDistMap = codeRuleDistList.stream().collect(Collectors.groupingBy(CodeRuleDist::getRuleId));
        final Map<Long, List<CodeRuleDetail>> codeRuleDetailsMap = codeRuleDetails.stream().collect(Collectors.groupingBy(CodeRuleDetail::getRuleDistId));

        contextParam.codeRuleContext = Context.builder().build();
        contextParam.codeRuleContext.codeRuleTlsMap = codeRuleTlsMap;
        contextParam.codeRuleContext.codeRuleDistMap = codeRuleDistMap;
        contextParam.codeRuleContext.codeRuleDetailsMap = codeRuleDetailsMap;

        List<CommonResult> resultList = new ArrayList<>(ruleList.size());
        for (CodeRule codeRule : ruleList) {
            CommonResult result = CommonResult.create(true).setTenantId(codeRule.getTenantId()).setRuleCode(codeRule.getRuleCode());
            try {
                contextParam.codeRuleContext.codeRule = codeRule;
                this.self().singleExecute(contextParam);
            } catch (Exception e) {
                result.setSuccessFlag(false).setErrorMsg(e.getMessage());
                e.printStackTrace();
            } finally {
                resultList.add(result);
            }
        }

        return resultList;
    }

    @Override
    public FunctionCode functionCode() {
        return FunctionCode.CODE_RULE;
    }

    @Override
    @DS(Const.UAT)
    @Transactional(rollbackFor = Exception.class)
    public void singleExecute(ContextParam contextParam) {
        CodeRule codeRule = contextParam.codeRuleContext.codeRule;
        Map<Long, List<CodeRuleTl>> codeRuleTlsMap = contextParam.codeRuleContext.codeRuleTlsMap;
        Map<Long, List<CodeRuleDist>> codeRuleDistMap = contextParam.codeRuleContext.codeRuleDistMap;
        Map<Long, List<CodeRuleDetail>> codeRuleDetailsMap = contextParam.codeRuleContext.codeRuleDetailsMap;

        final Long devId = codeRule.getRuleId();
        codeRule.setRuleId(null);
        this.codeRuleService.uatInsertCodeRule(ListUtil.toList(codeRule));

        final List<CodeRuleTl> codeRuleTls = codeRuleTlsMap.get(devId);
        if (CollUtil.isNotEmpty(codeRuleTls)) {
            codeRuleTls.forEach(x -> x.setRuleId(codeRule.getRuleId()));
            this.codeRuleService.uatInsertCodeRuleTl(codeRuleTls);
        }

        final List<CodeRuleDist> codeRuleDistList = codeRuleDistMap.get(devId);
        if (CollUtil.isNotEmpty(codeRuleDistList)) {
            codeRuleDistList.forEach(CodeRuleDist::dataFix);
            codeRuleDistList.forEach(x -> x.setRuleId(codeRule.getRuleId()));
            codeRuleDistList.forEach(x -> x.setRuleDistId(null));
            this.codeRuleService.uatInsertCodeRuleDist(codeRuleDistList);

            List<CodeRuleDetail> detailList = new ArrayList<>(10);
            for (CodeRuleDist dist : codeRuleDistList) {
                final List<CodeRuleDetail> details = codeRuleDetailsMap.get(dist.getDistId());
                if (CollUtil.isEmpty(details)) {
                    continue;
                }
                details.forEach(x -> x.setRuleDistId(dist.getRuleDistId()));
                detailList.addAll(details);
            }
            if (CollUtil.isNotEmpty(detailList)) {
                detailList.forEach(x -> x.setRuleDetailId(null));
                this.codeRuleService.uatInsertCodeRuleDetail(detailList);
            }
        }


    }

    @Builder
    @SuppressWarnings("all")
    public static class Context {
        public CodeRule codeRule;
        public Map<Long, List<CodeRuleTl>> codeRuleTlsMap;
        public Map<Long, List<CodeRuleDist>> codeRuleDistMap;
        public Map<Long, List<CodeRuleDetail>> codeRuleDetailsMap;

    }

}
